"use client"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; import { PageLoadingSkeleton } from "@/components/Componentes/page-loading-skeleton"; import NavigationButton from "@/components/Componentes/navigation-button"; import { PageBackground } from "@/components/Componentes/page-background"; import { hasQuestionAnswerValue, QuestionAnswersProvider, useQuestionAnswers, } from "@/components/Componentes/question-answer-storage"; import QuestionExitNavigationButton from "@/components/Componentes/question-exit-navigation-button"; import QuestionRenderer from "@/components/Componentes/question-renderer"; import QuestionSectionFlow from "@/components/Componentes/question-section-flow"; import StickyHeader from "@/components/Componentes/sticky-header"; import TestIntroPage from "@/components/Componentes/test-intro-page"; import TestQuestionsFlow, { type TestQuestion, } from "@/components/Componentes/test-questions-flow"; import { useGlasserQuestionsQuery, useSubmitGlasserAssessmentMutation, } from "@/hooks/marriage/use-glasser"; import { useCattellQuestionsQuery, useSubmitCattellAssessmentMutation, } from "@/hooks/marriage/use-cattell"; import { useFormSchemaQuery } from "@/hooks/marriage/use-form-schema"; import { convertSchemaToFrontendItems, type QuestionField } from "@/lib/schema-adapter"; import { defaultLocale, type Locale } from "@/translations/config"; import { useI18n } from "@/translations/provider"; type QuestionDetailClientProps = { closeLabel: string; continueLabel: string; description: string; informationLabel: string; itemSlug: string; locale?: Locale; questionsListHref: string; title: string; }; type StoredQuestionField = { label?: string; value?: unknown; type?: string; key?: string; }; type StoredAnswers = { fields?: StoredQuestionField[]; }; function getTestDraftStorageKey(slug: string) { return `marriage:tests:${slug}:draft`; } function getQuestionStorageKey(slug: string) { return `marriage:sections:${slug}:answers`; } function QuestionFlowWrapper({ visibleQuestions, itemSlug, dobQuestion, continueLabel, questionsListHref, }: { visibleQuestions: QuestionField[]; itemSlug: string; dobQuestion?: QuestionField; requiredQuestionsCount: number; continueLabel: string; questionsListHref: string; }) { const { getAnswerValue } = useQuestionAnswers(); // dynamicQuestions is now exactly what the backend gives as visible const dynamicQuestions = visibleQuestions; const requiredCount = useMemo( () => dynamicQuestions.filter((q) => q.required).length, [dynamicQuestions], ); return ( question.required ? [] : [index], )} questions={dynamicQuestions} > {dynamicQuestions.map((question, index) => { const answer = getAnswerValue(question); const hasAnswer = hasQuestionAnswerValue(answer ?? null); let isAnswered = hasAnswer; if (hasAnswer) { const isEmailQuestion = question.type === "email" || question.validation?.format === "email"; if (isEmailQuestion) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; isAnswered = emailRegex.test(String(answer).trim()); } else if (question.type === "birthplace") { const strVal = String(answer); const parts = strVal.split(",").map((p) => p.trim()); isAnswered = parts.length >= 2 && parts[0].length > 0 && parts[1].length > 0; } else if (question.type === "checkbox") { isAnswered = Array.isArray(answer) ? answer.length > 0 : hasAnswer; } } return (
); })}
); } export default function QuestionDetailClient({ closeLabel, continueLabel, description, informationLabel, itemSlug, locale = defaultLocale, questionsListHref, title, }: QuestionDetailClientProps) { const router = useRouter(); const { dictionary: t } = useI18n(); const [isTestStarted, setIsTestStarted] = useState(false); const [hasTestProgress, setHasTestProgress] = useState(false); useEffect(() => { if (typeof window !== "undefined") { const draftKey = `marriage:tests:${itemSlug}:draft`; const draftRaw = window.localStorage.getItem(draftKey); if (draftRaw) { try { const parsed = JSON.parse(draftRaw); if ( parsed && typeof parsed.answers === "object" && parsed.answers !== null && Object.keys(parsed.answers).length > 0 ) { setHasTestProgress(true); return; } } catch {} } setHasTestProgress(false); } }, [itemSlug, isTestStarted]); const { data: schema, isLoading: isSchemaLoading } = useFormSchemaQuery("profile", locale); const items = useMemo(() => convertSchemaToFrontendItems(schema, locale), [schema, locale]); const item = items.find((i) => i.slug === itemSlug); const isCattellSlug = itemSlug === "personality_test"; const isGlasserSlug = itemSlug === "glasser_5_needs_test"; const cattellQuery = useCattellQuestionsQuery(locale, { enabled: isCattellSlug && isTestStarted, retry: 0, }); const submitCattellMutation = useSubmitCattellAssessmentMutation(); const glasserQuery = useGlasserQuestionsQuery(locale, { enabled: isGlasserSlug && isTestStarted, retry: 0, }); const submitGlasserMutation = useSubmitGlasserAssessmentMutation(); const cattellTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = cattellQuery.data?.questions || []; // Strict schema validation for Cattell const isValidCattell = (q: any) => q.question_number && q.text && q.options && q.options.length === 3 && q.options.every((o: any) => o.id && o.label && o.value); if (questionsList.length > 0 && !questionsList.every(isValidCattell)) { console.error("Invalid Cattell API response schema"); return []; } return questionsList.map((q) => ({ id: q.question_number, text: q.text, options: q.options || [], })); }, [cattellQuery.data]); const glasserTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = glasserQuery.data?.questions || []; // Strict schema validation for Glasser const isValidGlasser = (q: any) => q.question_number && q.text && q.options && q.options.length === 5 && q.options.every((o: any) => o.id && o.label && typeof o.value === 'number' && o.value >= 1 && o.value <= 5); if (questionsList.length > 0 && !questionsList.every(isValidGlasser)) { console.error("Invalid Glasser API response schema"); return []; } return questionsList.map((q) => ({ id: q.question_number, text: q.text, info: "factor" in q ? (q.factor as string) : "factor_code" in q ? (q.factor_code as string) : undefined, options: q.options || [], })); }, [glasserQuery.data]); const visibleQuestions = useMemo(() => { if (!item) { return []; } return item.questions .filter((question) => (question as any).isVisible !== false) .map((question) => ({ ...question, required: Boolean(question.required), })); }, [item]); const requiredQuestionsCount = useMemo( () => visibleQuestions.filter((q) => q.required).length, [visibleQuestions], ); useEffect(() => { if (!isSchemaLoading && !item) { router.replace(questionsListHref); } }, [isSchemaLoading, item, questionsListHref, router]); if (isSchemaLoading) { return ( ); } else if (!item) { return null; } if (item && item.questions.length === 0) { if (isTestStarted) { const isQuestionsLoading = isCattellSlug ? cattellQuery.isLoading : isGlasserSlug ? glasserQuery.isLoading : false; if (isQuestionsLoading) { return ; } const activeTestQuestions = isCattellSlug ? cattellTestQuestions : isGlasserSlug ? glasserTestQuestions : []; if (activeTestQuestions.length === 0) { const isError = isCattellSlug ? cattellQuery.isError : isGlasserSlug ? glasserQuery.isError : false; const refetch = isCattellSlug ? cattellQuery.refetch : glasserQuery.refetch; return ( <>

{isError ? locale === "fa" ? "ط®ط·ط§ ط¯ط± ط¯ط±غŒط§ظپطھ ط³ظˆط§ظ„ط§طھ ط§ط² ط³ط±ظˆط±. ظ„ط·ظپط§ظ‹ ط§ط² ط§طھطµط§ظ„ ط§غŒظ†طھط±ظ†طھ غŒط§ ظˆط±ظˆط¯ ط¨ظ‡ ط­ط³ط§ط¨ ع©ط§ط±ط¨ط±غŒ ط§ط·ظ…غŒظ†ط§ظ† ط­ط§طµظ„ ع©ظ†غŒط¯." : "Failed to load questions from server. Please check your connection or login status." : locale === "fa" ? "ط³ظˆط§ظ„ط§طھغŒ ط¨ط±ط§غŒ ط§غŒظ† ط¢ط²ظ…ظˆظ† غŒط§ظپطھ ظ†ط´ط¯." : "No questions found for this test."}

); } const handleTestFinish = async ( answers: Record, ) => { if (isCattellSlug) { const responses = Object.entries(answers).map(([qNum, option]) => ({ question_number: Number(qNum), option: String(option), })); await submitCattellMutation.mutateAsync({ responses }); try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } else if (isGlasserSlug) { const responses = Object.entries(answers).map(([qNum, score]) => ({ question_number: Number(qNum), score: Number(score), })); await submitGlasserMutation.mutateAsync({ responses }); try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } }; return ( setIsTestStarted(false)} onFinish={handleTestFinish} draftStorageKey={getTestDraftStorageKey(item.slug)} /> ); } const bulletKey = item.slug === "glasser_5_needs_test" ? "glasser" : "personality"; const bullets = bulletKey === "glasser" ? [ t[ "Understanding your five basic needs helps you recognize what truly drives your behavior in relationships." ], t[ "Taking this test is not mandatory, but it will help you better understand your priorities and find a more compatible spouse." ], t[ "By identifying your strongest needs, you can better communicate your expectations and build healthier relationships." ], ] : [ t[ "Do you struggle to understand the underlying psychological drivers that dictate your interpersonal connections and communication barriers?" ], t[ "Taking this test is not mandatory, but it will help you better search for a spouse. The Personality test is a test for self-knowledge and better understanding of your spouse." ], t[ 'Do you operate based on superficial behavioral adaptations, or are you aware of the deep "source traits" that fundamentally control your decision-making processes?' ], ]; return ( <>

{item.title}

{ setIsTestStarted(true); }} > {isCattellSlug ? (
    {[ { title: t["Understanding Personality Traits"], desc: t[ "Helps provide an overall picture of traits such as sociability, independence, emotional sensitivity, and interpersonal style." ], }, { title: t["Assessing Communication Style"], desc: t[ "Shows how a person typically communicates, expresses emotions, and builds closeness in relationships." ], }, { title: t["Understanding Responses to Stress and Conflict"], desc: t[ "Offers insight into emotional stability, tension levels, and how a person may react in difficult or stressful situations." ], }, { title: t["Assessing Independence and Decision-Making"], desc: t[ "Helps identify the person’s level of independence, assertiveness, and preference for individual or shared decision-making." ], }, { title: t["Identifying Potential Differences and Challenges"], desc: t[ "Comparing two individuals’ results can highlight personality differences that may require attention in a long-term relationship." ], }, { title: t["Supporting Better Match Recommendations"], desc: t[ "Alongside interviews and other relationship criteria, personality assessment can help make the matching process more targeted and improve the evaluation of compatibility." ], }, ].map((bullet, index) => (
  • {bullet.title}

    {bullet.desc}

  • ))}
) : isGlasserSlug ? (
    {[ { title: t["Understanding Core Psychological Needs"], desc: t[ "Helps identify the importance of the five basic needs—love and belonging, power, freedom, fun, and survival—in each person’s life." ], }, { title: t["Recognizing Relationship Expectations"], desc: t[ "Provides insight into what each person expects from a relationship, such as closeness, independence, security, achievement, or shared enjoyment." ], }, { title: t["Assessing Personality Compatibility"], desc: t[ "Helps compare personality traits, behavioral tendencies, and interaction styles to identify areas of compatibility between two individuals." ], }, { title: t["Identifying Potential Sources of Conflict"], desc: t[ "Differences in needs or personality styles can highlight areas where misunderstandings, tension, or disagreements may arise in the relationship." ], }, { title: t["Improving Mutual Understanding"], desc: t[ "Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities." ], }, { title: t["Supporting More Suitable Match Recommendations"], desc: t[ "Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched." ], }, ].map((bullet, index) => (
  • {bullet.title}

    {bullet.desc}

  • ))}
) : null}
); } const dobQuestion = visibleQuestions.find( (question) => question.ui_config?.isDob === true || question.type === "date", ); return ( <>

{item.title}

); }